﻿using System;
using System.Collections.Generic;
using System.Linq;
using Director.DomainModels;

namespace Director.Indicators.Basic
{
	/// <summary>
	/// Фракталы.
	/// </summary>
	/// <remarks>
	/// http://ta.mql4.com/indicators/bills/fractal
	/// </remarks>
	public class ChartFractals : SignChartIndicator
	{
		// Номер центральной свечи
		private int _numCenter;

		// Флаг роста low'a до буфера
		private bool _wasLowUp;

		// Флаг снижения high'я до буфера
		private bool _wasHighDown;

		/// <summary>
		/// Создать <see cref="ChartFractals"/>.
		/// </summary>
		public ChartFractals() : this(5)
		{
		}

		/// <summary>
		/// Создать <see cref="ChartFractals"/>.
		/// </summary>
		/// <param name="length">Длина периода, не должна быть четной.</param>
		public ChartFractals(int length)
		{
			if (length%2 == 0)
			{
				throw new ArgumentOutOfRangeException("length", length, "Длина не должна быть четной");
			}

			_length = length;
			_numCenter = length/2;
		}

		private int _length;

		/// <summary>
		/// Длина периода.
		/// </summary>
		public int Length
		{
			get { return _length; }
			set
			{
				_length = value;
				_numCenter = value/2;
			}
		}

		public override string ShortKindName
		{
			get { return "FRACT"; }
		}

		public override void Calc(Candle[] candles)
		{
			var buffer = new List<Candle>();
			var result = new List<FractalSign>();

			for (var index = 0; index < candles.Length; index++)
			{
				var candle = candles[index];

				buffer.Add(candle);

				// если буфер стал достаточно большим (стал больше длины)
				if (buffer.Count > Length)
				{
					// Запоминаем интересующие изменения свечек до буфера
					if (buffer[0].High > buffer[1].High)
						_wasHighDown = true;
					else if (buffer[0].High < buffer[1].High)
						_wasHighDown = false;

					if (buffer[0].Low < buffer[1].Low)
						_wasLowUp = true;
					else if (buffer[0].Low > buffer[1].Low)
						_wasLowUp = false;

					buffer.RemoveAt(0);
				}
				else if (buffer.Count < Length) continue;

				// Логика расчета: последующие/предыдущие значения должны быть меньше/больше центрального значения

				// флаги для расчета фракталов (если флаг равен false, то на центральной свече нет фрактала)
				var isMax = true;
				var isMin = true;

				var centerHighPrice = buffer[_numCenter].High;
				var centerLowPrice = buffer[_numCenter].Low;

				for (var i = 0; i < buffer.Count; i++)
				{
					if (i == _numCenter)
						continue;

					// Все значения до и после центральной свечи должны быть
					// больше для фрактала вверх и меньше для фрактала вниз
					if (isMax && buffer[i].High > centerHighPrice)
					{
						isMax = false;
						if (!isMin) break;
					}
					if (isMin && buffer[i].Low < centerLowPrice)
					{
						isMin = false;
						if (!isMax) break;
					}
				}

				// Фильтр для ситуаций где цена у экстремума одинакова за период больше длины буфера
				if (isMax && _wasHighDown && buffer.GetRange(0, _numCenter).Min(i => i.High) == centerHighPrice)
				{
					isMax = false;
				}

				if (isMin && _wasLowUp && buffer.GetRange(0, _numCenter).Max(i => i.Low) == centerLowPrice)
				{
					isMin = false;
				}

				var shift = buffer.Count - _numCenter - 1;
				var last = result.LastOrDefault();
				if (isMax)
				{
					if (last != null && result.Last().Text == "^")
					{
						if (last.Value < centerHighPrice)
						{
							last.Value = centerHighPrice;
							last.DateTime = buffer[_numCenter].Date;
						}
					}
					else
					{
						result.Add(new FractalSign {DateTime = buffer[shift].Date, Value = centerHighPrice, Text = "^"});
					}
				}
				if (isMin)
				{
					if (last != null && result.Last().Text == "v")
					{
						if (last.Value > centerLowPrice)
						{
							last.Value = centerLowPrice;
							last.DateTime = buffer[_numCenter].Date;
						}
					}
					else
					{
						result.Add(new FractalSign { DateTime = buffer[_numCenter].Date, Value = centerLowPrice, Text = "v" });
					}
				}
				//var upValue = isMax ? new ShiftedIndicatorValue(this, shift, new DecimalIndicatorValue(this, centerHighPrice)) : new ShiftedIndicatorValue(this);
				//var downValue = isMin ? new ShiftedIndicatorValue(this, shift, new DecimalIndicatorValue(this, centerLowPrice)) : new ShiftedIndicatorValue(this);

				//upValue.IsFinal = _input.IsFinal;
				//downValue.IsFinal = _input.IsFinal;

				//var complexValue = new ComplexIndicatorValue(this);
				//complexValue.InnerValues.Add(Up, Up.Process(upValue));
				//complexValue.InnerValues.Add(Down, Down.Process(downValue));
			}

			Signs = result.ToArray();
		}
	}

	public class FractalSign : Sign
	{
		
	}	
}
